#include "graphics/directx_renderer.h"

namespace graphics{

int DirectxRenderer::Initialize(const bool & fullscreen, const uint32 & width, const uint32 & height){

    instance_ = Direct3DCreate9( D3D_SDK_VERSION );
    if(!instance_)
        return base::ER_ERROR;  //TODO: make specific error code

    int result = InitializePresentParameters(fullscreen, width, height);
    if( result_code::IsError(result) ) {
        if(instance_)
            instance_->Release();
        return result;
    }

    //Next make sure that the hardware has support for vertex processing
    //If it does not, create a device that will perform vertex processing 
    //  in software.
    D3DCAPS9 caps;
    instance_->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps );
    DWORD vertex_processing = 0;
    if ( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
        vertex_processing = D3DCREATE_HARDWARE_VERTEXPROCESSING;
    else
        vertex_processing = D3DCREATE_SOFTWARE_VERTEXPROCESSING;    

    if( FAILED(instance_->CreateDevice(D3DADAPTER_DEFAULT, 
                                       D3DDEVTYPE_HAL, 
                                       window_, 
                                       vertex_processing, 
                                       &present_parameters_, 
                                       &device_) ) ) {
        if(instance_)
            instance_->Release();
        return base::ER_ERROR;  //TODO: make specific error code
    }

    return base::OK_OK;
}

int DirectxRenderer::InitializePresentParameters(const bool & fullscreen, const uint32 & width, const uint32 & height) {
    //Even though we set all of it's members, it's still good practice to zero it out
    ZeroMemory(&present_parameters_ ,sizeof(D3DPRESENT_PARAMETERS));

    present_parameters_.BackBufferCount = 1;                                    //We only need a single back buffer
    present_parameters_.MultiSampleType = D3DMULTISAMPLE_NONE;                  //No multi-sampling
    present_parameters_.MultiSampleQuality = 0;                                 //No multi-sampling
    present_parameters_.SwapEffect = D3DSWAPEFFECT_DISCARD;                     // Throw away previous frames, we don't need them
    present_parameters_.hDeviceWindow = window_;                                //This is our main (and only) window
    present_parameters_.Flags = 0;                                              //No flags to set
    present_parameters_.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;   //Default Refresh Rate
    present_parameters_.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;     //Default Presentation rate
    present_parameters_.BackBufferWidth 	= width;
	present_parameters_.BackBufferHeight    = height;
    int result;
    
    result = FindSupportedAdapterFormat();
    if( result_code::IsError(result) )
        return result;
    
    result = FindSupportedStencilFormat();
    if( result_code::IsError(result) )
        return result;

    fullscreen_ = fullscreen;	
    present_parameters_.Windowed  = (fullscreen) ? FALSE : TRUE;    	

    return base::OK_OK;
}

 
int DirectxRenderer::Shutdown(){
    //Release objects in the reverse order they were created

    if(device_)
        device_->Release();

    if(instance_)
        instance_->Release();

    return base::OK_OK;
}

int DirectxRenderer::Reset() {
    HRESULT hr;

    hr= device_->TestCooperativeLevel();

    if(hr == D3DERR_DEVICELOST) {   //Device is lost and cannot be reset yet
        Sleep(500); //Wait a bit so we don't burn through cycles for no reason        
    } else if(hr == D3DERR_DEVICENOTRESET) { //Lost but we can reset it now
        hr= device_->Reset(&present_parameters_);
        if(SUCCEEDED(hr))
            return base::OK_TRUE;
        else return base::ER_ERROR; //TODO: make specific error code            
    }
    
    return base::OK_FALSE;
}

int DirectxRenderer::ToFullcreen(){
    if(!fullscreen_) {
        present_parameters_.Windowed = FALSE;
        HRESULT hr = device_->Reset(&present_parameters_);

        if(SUCCEEDED(hr)){
            fullscreen_ = true;
            return base::OK_TRUE;
        }
        else return base::ER_ERROR; //TODO: make specific error
    }
    else return base::OK_FALSE;
}

int DirectxRenderer::ToWindowed() {
    if(fullscreen_) {
        present_parameters_.Windowed = TRUE;
        HRESULT hr = device_->Reset(&present_parameters_);

        if(SUCCEEDED(hr)) {
            fullscreen_ = false;
            return base::OK_TRUE;
        }
        else return base::ER_ERROR; //TODO: make specific error
    }
    else return base::OK_FALSE;
}

int DirectxRenderer::ChangeResolution(int width, int height) {return base::ER_ERROR;}

int DirectxRenderer::Render(const Camera * camera){
    device_->Clear(0,0,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,0x00000000,1.0f,0);

    HRESULT hr = device_->BeginScene();
    if(FAILED(hr))
        return 

    device_->EndScene();
    device_->Present( NULL, NULL, NULL, NULL );
    return 0;
}
 
int DirectxRenderer::FindSupportedAdapterFormat() {

    present_parameters_.BackBufferFormat = D3DFMT_UNKNOWN;

    if(present_parameters_.Windowed) {        
        return base::OK_OK;
    }

    const D3DFORMAT format_list[]={D3DFMT_A8R8G8B8, D3DFMT_X8R8G8B8,  D3DFMT_A1R5G5B5, D3DFMT_X1R5G5B5, D3DFMT_R5G6B5, };
    const int format_count = sizeof(format_list)/sizeof(D3DFORMAT);

    for(int i = 0; i < format_count; i++) {
        if(SUCCEEDED(instance_->CheckDeviceType(D3DADAPTER_DEFAULT, //Test the primary display device
                             D3DDEVTYPE_HAL,        //Only evaluate modes supported in hardware
                             format_list[i],        //The is the primary (viewable) buffer format
                             format_list[i],        //This is the back (drawable) buffer format
                             FALSE ) ) ) {          //Just checking fullscreen modes
            present_parameters_.BackBufferFormat = format_list[i];
            return base::OK_OK;
        }        
    }

    return base::ER_ERROR; //TODO: make specific error code
}
 
int DirectxRenderer::FindSupportedStencilFormat() {

    const D3DFORMAT format_list[]={D3DFMT_D24S8,D3DFMT_D24X8, D3DFMT_D16};
    const int format_count = sizeof(format_list)/sizeof(D3DFORMAT);

    present_parameters_.EnableAutoDepthStencil = FALSE;

    for(int i = 0; i <format_count; i++) {
        if ( SUCCEEDED( instance_->CheckDeviceFormat( D3DADAPTER_DEFAULT, //Test the primary display device
                                                      D3DDEVTYPE_HAL, 
                                                      present_parameters_.BackBufferFormat, 
                                                      D3DUSAGE_DEPTHSTENCIL, 
                                                      D3DRTYPE_SURFACE, 
                                                      format_list[i] ) ) ) {
            present_parameters_.AutoDepthStencilFormat = format_list[i];
            present_parameters_.EnableAutoDepthStencil = TRUE;
            return base::OK_TRUE;
        }
    }

    return base::ER_ERROR; //TODO: make specific error code
}
 
}//namespace graphics